/**
 * 9.6.2 枚举类型
 */
function enumeration(nameSpace) {
    enumeration = function () {
        throw new Error('dont construct')
    }
    let proto = enumeration.prototype = {
        constructor: enumeration,
        toString: function () {
            return this.name;
        },
        toJSON: function () {
            return this.name;
        },
        valueOf: function () {
            return this.value;
        }
    }
    enumeration.values = [];
    for (let name in nameSpace) {
        let e = Object.create(proto);
        e.name = name;
        e.value = nameSpace[name];
        enumeration[name] = e;
        enumeration.values.push(e);
    }
    enumeration.foreach = function (f, c) {
        enumeration.values.forEach((item, index) => {
            f.call(c, item, index);
        })
    }
    return enumeration;
}
function enumeratedDemo() {
    let Coin = enumeration({Penny: 1, Nickel: 5, Dime: 10, Quarter: 25})
    let c = Coin.Penny;
    console.log(c instanceof Coin);
    console.log(c.constructor === Coin);
    console.log(Coin.Penny == 1)
    window.Penny = c;
}
// enumeratedDemo()

function Set() {
    this.values = {};
    this.n = 0;
    this.add.apply(this, arguments);
}

Set.prototype.add = function () {
    let length = arguments.length;
    for (let i = 0; i < length; i++) {
        let value = arguments[i];
        let key = Set.v2s(value)
        if (!this.values.hasOwnProperty(key)) {
            this.n++;
        }
        this.values[key] = value;
    }
    return this;
}

Set.prototype.remove = function () {
    let length = arguments.length;
    for (let i = 0; i < length; i++) {
        let key = Set.v2s(arguments[i]);
        if (this.values.hasOwnProperty(key)) {
            delete this.values[key];
            this.n--;
        }
    }
    return this;
}

Set.prototype.foreach = function (f, context) {
    for (const valuesKey in this.values) {
        f.call(context, this.values[valuesKey]);
    }
};

Set.v2s = function (val) {
    switch (val) {
        case undefined:
            return 'u';
        case null:
            return 'n';
        case true:
            return 't';
        case false:
            return 'f';
        default:
            switch (typeof val) {
                case 'number':
                    return `#${val}`;
                case 'string':
                    return `"${val}`;
                default:
                    return `@${objectId(val)}`;
            }
    }

    function objectId(val) {
        //私有属性用来存放id
        let prop = "|**objectId**|";
        if (!val.hasOwnProperty(prop)) {
            val[prop] = Set.v2s.next++;
        }
        return val[prop];
    }
}
Set.v2s.next = 100;


/**
 * 继承的实现
 **/

function defineSubclass(superclass, constructor, methods, statics) {
    if(superclass) {
        constructor.prototype = Object.create(superclass.prototype);
        constructor.prototype.constructor = constructor;
    }
    if(methods) extend(constructor.prototype, methods);
    if(statics) extend(constructor, statics);

    return constructor;
}

Function.prototype.extend = function (constructor, methods, statics) {
    return defineSubclass(this, constructor, methods, statics);
};

function SingletonSet(member) {
    this.member = member;
}

SingletonSet.prototype = Object.create(Set.prototype);

function NonNullSet() {
    Set.apply(this, arguments);
}

NonNullSet.prototype = Object.create(Set.prototype);
NonNullSet.prototype.constructor = NonNullSet;

NonNullSet.prototype.add = function () {
    let length = arguments.length;
    for (let i = 0; i < length; i++) {
        if (arguments[i] == null) {
            throw new Error('cant add null or undefined');
        }
    }
    return Set.prototype.add.apply(this, arguments);
};

/**
 * 这个函数可以通过filter 增强add方法 生成一个构造函数,继承与superClass
 * @param superClass
 * @param filter
 * @returns {constructor}
 */
function filteredSetSubclass(superClass, filter) {
    let constructor = function () {
        superClass.apply(this, arguments);
    };
    let proto = constructor.prototype = Object.create(superClass.prototype);
    proto.constructor = constructor;
    proto.add = function () {
        let length = arguments.length;
        for (let i = 0; i < length; i++) {
            let argument = arguments[i];
            if (!filter(argument)) {
                throw new Error(`value ${argument} reject by filter`)
            }
        }
        superClass.prototype.add.apply(this, arguments);
    };
    return constructor;
}

/**
 * 使用Function extend 创建子类
 */
let NotNullSetByFunctionExtend = (function () {
    let superClass = Set;
    return superClass.extend(function () {
        superClass.apply(this, arguments);
    },{
        add() {
            let length = arguments.length;
            for (let i = 0; i < length; i++) {
                let val = arguments[i];
                if (val == null) {
                    throw new Error('member dont null or undefined');
                }
            }
            superClass.prototype.add.apply(this, arguments);
        }
    });
})()

function AbstractMethod() {
    throw new Error("abstract method");
}

function AbstractSet() {
    throw new Error("Cant instantiate abstract classes");
}